home *** CD-ROM | disk | FTP | other *** search
- page 58,132
- ;****************************************************************
- ;* *
- ;* Digitized Voice Programmer's Toolkit *
- ;* ------------------------------------ *
- ;* *
- ;* Sound Playback Primitives *
- ;* *
- ;* Copyright (c) 1991, Farpoint Software *
- ;* *
- ;****************************************************************
-
- .8086
- .MODEL LARGE
-
- ;**********************************************************************
- ;* *
- ;* Equates *
- ;* *
- ;**********************************************************************
-
- tccount equ 72 ;reload count to produce 16572 Hz
- countmax_18hz equ 910 ;ratio of new timer rate to standard rate
-
- tcaddrc equ 43h ;timer/counter control register address
- tcaddrd0 equ 40h ;timer/counter data register zero (sys clock)
- tcaddrd2 equ 42h ;timer/counter data register two (spkr)
-
- tcmode0a equ 34h ;two-byte mode for system clock timer
- tcmode0b equ 24h ;high-byte mode for system clock timer
- tcmode2a equ 0B0h ;mode for speaker timer (for initialization)
-
- tclatchcmd0 equ 00h ;latch command for reading timer 0
- tclatchcmd2 equ 80h ;latch command for reading timer 2
-
- ppiaddr equ 61h ;programmable peripheral interface address
-
- kbdonly_mask equ 0FDh ;mask config to allow only keyboard interrupt
- intmaskaddr equ 21h ;address of the interrupt mask register
- kbdint_number equ 9 ;vector number of the keyboard hardware int
-
- ;CPU speed calibration initialization parameters
-
- init_delayctr equ 1024
- init_cal_increment equ 512
- init_cal_passes equ 10
-
- ;**********************************************************************
- ;* *
- ;* Global variables *
- ;* *
- ;**********************************************************************
-
- .DATA
-
- ;CPU speed calibration data
-
- delayctr dw init_delayctr-1
- cal_increment dw init_cal_increment
- cal_passes dw init_cal_passes
-
- ;keyboard hardware interrupt occurrence flag
-
- keyflag db 0
-
- ;interrupt controller mask save byte
-
- original_mask db 0
-
- ;port 61h default configuration
-
- ppibyte db 0
-
- ;**********************************************************************
- ;* *
- ;* Code *
- ;* *
- ;**********************************************************************
-
- .CODE
-
- assume ds:DGROUP
-
- ;code-segment storage for keyboard interrupt chain vector
-
- original_kbdint_vec dd 0
-
- ;**********************************************************************
- ;* Keyboard Hardware Interrupt Intercept Routine *
- ;**********************************************************************
-
- ;This routine sets the "keyflag" variable to 1 whenever a keyboard
- ; interrupt occurs.
-
- kbdint proc far
-
- push ds
- push ax
- mov ax,DGROUP
- mov ds,ax
- mov keyflag,1
- pop ax
- pop ds
- jmp dword ptr cs:original_kbdint_vec
-
- kbdint endp
-
- ;**********************************************************************
- ;* Calibrated software delay routine *
- ;**********************************************************************
-
- ;lots of nops
-
- delay proc near
-
- db init_delayctr dup (90h)
- ret ;starting delay value
- db (init_delayctr-1) dup (90h)
- ret ;should never actually be executed
-
- delay endp
-
- ;**********************************************************************
- ;* Initialize timer for correct speaker operation. *
- ;**********************************************************************
-
- ;This procedure returns 0 if successful and 1 if unsuccessful.
-
- set_ppibyte proc near
-
- ;return value is kept in BX register
-
- mov bx,0 ;set to 1 later if required
-
- ;set gate 2 drive to one
-
- in al,ppiaddr
- and al,0FDh
- or al,01h
- mov ppibyte,al
- out ppiaddr,al
-
- ;set timer chip to mode zero (int on terminal count)
-
- mov al,tcmode2a
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
-
- ;set count to 0001h for quick termination
-
- mov al,1
- out tcaddrd2,al
- jmp short $+2
- jmp short $+2
- sub al,al
- out tcaddrd2,al
- sti
- jmp short $+2
- jmp short $+2
-
- ;write the standard setup to timer zero
-
- mov al,tcmode0a
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- mov al,0
- out tcaddrd0,al
- jmp short $+2
- jmp short $+2
- out tcaddrd0,al
- sti
- jmp short $+2
- jmp short $+2
-
- ;set timer zero to high-byte mode
-
- mov al,tcmode0b
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- mov al,0
- out tcaddrd0,al
- sti
- jmp short $+2
- jmp short $+2
-
- ;wait for next timer tick
-
- mov ah,0
- mov al,tclatchcmd0
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- sti
- jmp short $+2
- jmp short $+2
- mov dx,ax
- spi_sync:
- mov al,tclatchcmd0
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- sti
- jmp short $+2
- cmp ax,dx
- mov dx,ax
- jbe spi_sync
-
- ;Wait for another timer tick (18Hz); this should be
- ; plenty of time for timer 2 to reach terminal count.
-
- spi_wt1:
- mov al,tclatchcmd0
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- sti
- sub ah,ah
- cmp ax,dx
- mov dx,ax
- jbe spi_wt1
-
- ;read the content of timer 2 and check for 0001h
-
- mov al,tclatchcmd2
- cli
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd2
- mov ah,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd2
- sti
- or al,al ;test high byte for 00h
- jnz spi_countdone
- cmp ah,1 ;test low byte for 01h
- jnz spi_countdone
-
- ;(arrive here if timer 2 is NOT counting)
- ;If timer 2 does not count,
- ; we flag an error and keep going.
-
- mov bx,1
-
- ;set gate 2 drive to zero (disable counting)
-
- spi_countdone:
- in al,ppiaddr
- and al,0FCh
- mov ppibyte,al
- out ppiaddr,al
-
- ;count is (supposedly) finished; test timer 2 output readback bit
-
- in al,ppiaddr
- test al,20h ;bit 5 should be high
- jnz spi_done
-
- ;wrong polarity, set error flag and continue
-
- mov bx,1
-
- spi_done:
- mov ax,bx
- ret
-
- set_ppibyte endp
-
- ;**********************************************************************
- ;* Set Calibration Constant *
- ;* *
- ;* This call simply sets the calibration constant to the *
- ;* value specified in the parameter. *
- ;**********************************************************************
-
- ;Accepts the following parameters with PASCAL parameter-passing convention:
-
- ;Position Size Description
- ;-------- ---- -----------
- ; 1 2 The calibration constant to be set.
-
- ;There is no return value.
-
- psc_parm1 equ [bp+6] ;length = 2
-
- psc_parmlength equ 2
-
- public PSETCAL
-
- PSETCAL proc far
-
- push bp
- mov bp,sp
-
- ;in case this routine has been called before, erase the "ret" instruction
- ; that was placed in the delay routine
-
- inc delayctr ;since we decremented it before
- lea bx,delay
- add bx,delayctr
- mov byte ptr cs:[bx],90h ;this is a "nop" instruction
-
- ;get the new delay parameter
-
- mov ax,psc_parm1
- mov delayctr,ax
-
- ;insert a "ret" instruction at the new location in the delay routine
-
- lea bx,delay
- add bx,delayctr
- mov byte ptr cs:[bx],0C3h ;this is a "ret" instruction
-
- dec delayctr ;for benefit of playback routine
-
- pop bp
- ret psc_parmlength
-
- PSETCAL endp
-
- ;**********************************************************************
- ;* Calibration procedure *
- ;* *
- ;* This routine must be called once before any calls are made *
- ;* to the playback routine. It measures the CPU execution *
- ;* speed and saves compensation values. *
- ;**********************************************************************
-
- ;There are no entry parameters.
-
- ;The return value is a double word, defined as follows:
-
- ;Return value low word Meaning
- ;--------------------- -------
- ; 0 success
- ; 1 this CPU is too slow to accomplish
- ; normal-speed playback
- ; 2 operation not possible under Windows
- ; in "Enhanced" mode
- ; 3 unusual speaker drive circuit encountered,
- ; might not output sound
-
- ;The return value high word is the actual speed calibration constant
- ; for this computer
-
- cal_temp1 equ [bp-2] ;length = 2
-
- cal_locallength equ 2
-
- public PCALIBRATE
-
- PCALIBRATE proc far
-
- push bp
- mov bp,sp
- sub sp,cal_locallength
- push si
- push di
-
- ;test for the "Enhanced Mode Windows" environment
-
- mov ax,1600h
- int 2Fh
- cmp al,00h
- je cal_no_enh_win
- cmp al,80h
- je cal_no_enh_win
- mov delayctr,1
- mov ax,2
- jmp cal_exit
- cal_no_enh_win:
-
- ;test speaker drive circuitry and set configuration
-
- call set_ppibyte
- mov cal_temp1,ax
-
- ;in case this routine has been called before, erase the "ret" instruction
- ; that was placed in the delay routine
-
- inc delayctr ;since we decremented it before
- lea bx,delay
- add bx,delayctr
- mov byte ptr cs:[bx],90h ;this is a "nop" instruction
-
- ;initialize parameters
-
- mov delayctr,init_delayctr
- mov cal_increment,init_cal_increment
- mov cal_passes,init_cal_passes
-
- ;insert a "ret" instruction into the starting location in the delay routine
-
- lea bx,delay
- add bx,delayctr
- mov byte ptr cs:[bx],0C3h ;this is a "ret" instruction
-
- ;no interrupts until we are through calibrating
-
- cli
-
- ;write the standard setup to timer zero
-
- mov al,tcmode0a
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- mov al,0
- out tcaddrd0,al
- jmp short $+2
- jmp short $+2
- out tcaddrd0,al
- jmp short $+2
- jmp short $+2
-
- ;set timer zero to high-byte mode
-
- mov al,tcmode0b
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- mov al,0
- out tcaddrd0,al
- jmp short $+2
- jmp short $+2
-
- ;initialize loop count
-
- cal_zerocnt:
- sub cx,cx
-
- ;synchronize to timer tick
-
- mov ah,0
- mov al,tclatchcmd0
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- jmp short $+2
- jmp short $+2
- mov si,ax
- cal_sync:
- mov al,tclatchcmd0
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- jmp short $+2
- cmp ax,si
- mov si,ax
- jbe cal_sync
-
- ;calibration loop
-
- cal_dummyloop:
- mov al,80h
- nop
- nop
- or ax,ax
- jz cal_scale
- cal_scale:
- sub ah,ah
- mov bx,1 ;dummy value
- mul bx
- mov al,ah
- mov ah,dl
- lea bx,delay
- add bx,ax
- mov word ptr cs:[bx],9090h ;these are "nop" instructions
- mov al,ppibyte
- or al,00h
- out ppiaddr,al
- and al,0FFh
- call delay
- mov word ptr cs:[bx],9090h ;these are "nop" instructions
- inc cx
- mov al,tclatchcmd0
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- sub ah,ah
- nop
- nop
- cmp ax,si
- mov si,ax
- nop
- nop
- jbe cal_dummyloop
-
- ;kill the old "ret" instruction in the delay routine
-
- lea bx,delay
- add bx,delayctr
- mov byte ptr cs:[bx],90h ;this is a "nop" instruction
-
- ;adjust delay counter
-
- mov ax,cal_increment
- shr cal_increment,1
- cmp cx,countmax_18hz
- je cal_setret
- jb cal_makefaster
- add delayctr,ax
- jmp cal_setret
- cal_makefaster:
- sub delayctr,ax
- cal_setret: ;here we insert a new return
- lea bx,delay
- add bx,delayctr
- mov byte ptr cs:[bx],0C3h ;this is a "ret" instruction
- dec cal_passes
- cmp cal_passes,0
- je cal_restore
- jmp cal_zerocnt
-
- ;repair loss of system timer ticks and restore interrupts
-
- cal_restore:
- mov bx,40h
- mov es,bx
- mov bx,6Ch
- add word ptr es:[bx],init_cal_passes+1
- adc word ptr es:[bx+2],0
- cmp word ptr es:[bx+2],18h
- jb cal_tc_end
- ja cal_tc_midnite
- cmp word ptr es:[bx],0B0h
- jb cal_tc_end
- cal_tc_midnite:
- mov byte ptr es:[bx+4],1
- sub word ptr es:[bx],0B0h
- sbb word ptr es:[bx+2],18h
- cal_tc_end:
- sti
-
- ;determine if CPU is too slow
-
- dec delayctr ;for benefit of playback routine
- cmp delayctr,64
- jb cal_cpuslow
- sub ax,ax
- jmp cal_exit
- cal_cpuslow:
- mov ax,1
- cal_exit:
- or ax,ax
- jnz cal_rdc
- cmp word ptr cal_temp1,0
- jz cal_rdc
- mov ax,3
- cal_rdc:
- mov dx,delayctr
- pop di
- pop si
- add sp,cal_locallength
- pop bp
- ret
-
- PCALIBRATE endp
-
- ;**********************************************************************
- ;* Playback procedure *
- ;**********************************************************************
-
- ;Accepts the following parameters with PASCAL parameter-passing convention:
-
- ;Position Size Description
- ;-------- ---- -----------
- ; 1 4 A far pointer to the memory block used for voice data.
- ; 2 4 A dword indicating the length of the memory block.
-
- ;The return value is a dword indicating the number of bytes actually played.
-
- pv_parm1 equ [bp+10] ;length = 4
- pv_parm2_hi equ [bp+8] ;length = 2
- pv_parm2_lo equ [bp+6] ;length = 2
-
- pv_parmlength equ 8
-
- pv_temp1 equ [bp-2] ;length = 2
- pv_temp2 equ [bp-4] ;length = 2
-
- pv_locallength equ 4
-
- public PLAYVOICE
-
- PLAYVOICE proc far
-
- push bp
- mov bp,sp
- sub sp,pv_locallength
- push si
- push di
-
- ;setup speaker and timer 2
-
- call set_ppibyte
-
- ;mask all interrupts except the keyboard
-
- cli
-
- in al,intmaskaddr
- mov original_mask,al
- mov al,kbdonly_mask
- out intmaskaddr,al
-
- ;install the keyboard interrupt intercept routine
-
- sub ax,ax
- mov es,ax
- mov bx,kbdint_number*4
- mov ax,es:[bx]
- mov word ptr cs:original_kbdint_vec,ax
- mov ax,es:[bx+2]
- mov word ptr cs:original_kbdint_vec+2,ax
- lea ax,kbdint
- mov es:[bx],ax
- mov ax,cs
- mov es:[bx+2],ax
-
- sti
-
- ;set up initial conditions
-
- mov keyflag,0
- les si,dword ptr pv_parm1
- mov cx,pv_parm2_lo
- mov di,pv_parm2_hi
- jcxz pv_playloop
- inc di
-
- ;playback loop
-
- pv_playloop:
- mov al,es:[si]
- inc si
- jz pv_incrseg
- pv_scale:
- sub ah,ah
- mov bx,delayctr
- mul bx
- mov al,ah
- mov ah,dl
- lea bx,delay
- add bx,ax
- mov word ptr cs:[bx],61E6h ;this is an "out 61h,al" instruction
- mov al,ppibyte
- or al,02h
- out ppiaddr,al
- and al,0FDh
- call delay
- mov word ptr cs:[bx],9090h ;these are "nop" instructions
- nop
- mov al,tclatchcmd0
- out tcaddrc,al
- jmp short $+2
- jmp short $+2
- in al,tcaddrd0
- sub ah,ah
- cmp keyflag,0
- jne pv_stop
- loop pv_playloop
- dec di
- jnz pv_playloop
- pv_stop:
- jmp short pv_fixkbi
-
- pv_incrseg:
- mov bx,es
- add bx,1000h
- mov es,bx
- jmp short pv_scale
-
- ;remove the keyboard interrupt intercept routine
-
- pv_fixkbi:
- cli
-
- sub ax,ax
- mov es,ax
- mov bx,kbdint_number*4
- mov ax,word ptr cs:original_kbdint_vec
- mov es:[bx],ax
- mov ax,word ptr cs:original_kbdint_vec+2
- mov es:[bx+2],ax
-
- ;restore the original interrupt mask
-
- mov al,original_mask
- out intmaskaddr,al
-
- sti
-
- ;leave speaker in a known state
-
- mov al,ppibyte
- out ppiaddr,al
-
- ;calculate the number of bytes played
-
- jcxz pv_calcbytes
- dec di
- pv_calcbytes:
- mov ax,pv_parm2_lo
- mov dx,pv_parm2_hi
- sub ax,cx
- sbb dx,di
- mov pv_temp2,ax
- mov pv_temp1,dx
-
- ;compensate for lost timer ticks
-
- mov cx,countmax_18hz
- div cx
- mov bx,40h
- mov es,bx
- mov bx,6Ch
- cli
- add es:[bx],ax
- adc word ptr es:[bx+2],0
- cmp word ptr es:[bx+2],18h
- jb pv_tc_end
- ja pv_tc_midnite
- cmp word ptr es:[bx],0B0h
- jb pv_tc_end
- pv_tc_midnite:
- mov byte ptr es:[bx+4],1
- sub word ptr es:[bx],0B0h
- sbb word ptr es:[bx+2],18h
- pv_tc_end:
- sti
-
- ;set the return value
-
- mov ax,pv_temp2
- mov dx,pv_temp1
-
- pop di
- pop si
- add sp,pv_locallength
- pop bp
- ret pv_parmlength
-
- PLAYVOICE endp
-
- end
-